이번 튜토리얼에서는 tf.data
를 이용해서 꽃 이미지를 불러와 사용해보겠습니다.
이 예시에서 사용된 데이터셋은 이미지의 디렉토리로 배포되며, 디렉토리당 하나의 이미지 클래스로 구성되어있습니다.
import warnings
warnings.simplefilter('ignore')
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import os
tf.__version__
훈련을 시작하기 전에 인식하고자 하는 새로운 클래스에 대해 네트워크를 가르치기 위한 이미지 세트가 필요할 것입니다. 우리는 구글에서 허가받은 꽃 사진들을 사용할 것입니다.
참고: 모든 이미지는 CC-BY 라이센스가 부여되며, 제작자는 “LICENSE.txt” 파일에 게시되어 있습니다.
import pathlib
data_dir = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',
fname='flower_photos', untar=True)
data_dir = pathlib.Path(data_dir)
다운로드(218MB)하면 이제 꽃 사진 복사본을 사용할 수 있을 것입니다.
이 디렉터리에는 클래스당 하나씩, 5개의 하위 디렉터리가 포함되어있습니다.
image_count = len(list(data_dir.glob('*/*.jpg')))
image_count
CLASS_NAMES = np.array([item.name for item in data_dir.glob('*') if item.name != "LICENSE.txt"])
CLASS_NAMES
각 디렉토리에는 위 클래스들과 같은 종류의 꽃의 이미지들이 들어 있습니다.
다음은 장미 사진을 여러개를 불러오겠습니다.
import IPython.display as display
from PIL import Image
roses = list(data_dir.glob('roses/*'))
for image_path in roses[:3]:
display.display(Image.open(str(image_path)))
keras.preprocessing
으로 이미지 불러오기tf.keras.preprocessing
을 이용하면 쉽게 이미지를 불러올 수 있습니다.
# 1./255는 0과 1사이의 unit8에서 float32 숫자로 바꿔주는 것입니다.
image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
이미지 로더에 대한 일부 매개 변수를 정의하겠습니다.
BATCH_SIZE = 32
IMG_HEIGHT = 224
IMG_WIDTH = 224
STEPS_PER_EPOCH = np.ceil(image_count/BATCH_SIZE)
train_data_gen = image_generator.flow_from_directory(directory=str(data_dir),
batch_size=BATCH_SIZE,
shuffle=True,
target_size=(IMG_HEIGHT, IMG_WIDTH),
classes = list(CLASS_NAMES))
배치(batch)를 확인합니다.
def show_batch(image_batch, label_batch):
plt.figure(figsize=(10,10))
for n in range(25):
ax = plt.subplot(5,5,n+1)
plt.imshow(image_batch[n])
plt.title(CLASS_NAMES[label_batch[n]==1][0].title())
plt.axis('off')
image_batch, label_batch = next(train_data_gen)
show_batch(image_batch, label_batch)
tf.data
로 이미지 불러오기위의 keras.preprocessing
방법은 편리하지만 다음과 같은 세 가지 단점이 있습니다.
파일들을 tf.data.Dataset
으로 불러오려면 먼저 파일 경로들의 데이터셋을 만들어야 합니다.
list_ds = tf.data.Dataset.list_files(str(data_dir/'*/*'))
for f in list_ds.take(5):
print(f.numpy())
파일 경로를 통해 (image_data, label) 쌍으로 변환하는 텐서플로우 함수를 만듭니다.
def get_label(file_path):
# 경로를 경로 구성요소 목록으로 변환합니다
parts = tf.strings.split(file_path, os.path.sep)
# 끝에서 두 번째 요소는 클래스 디렉터리입니다.
return parts[-2] == CLASS_NAMES
def decode_img(img):
# 압축된 문자열을 3D uint8 텐서로 변환합니다
img = tf.image.decode_jpeg(img, channels=3)
# `convert_image_dtype`은0~1 사이의 float 값으로 변환해줍니다.
img = tf.image.convert_image_dtype(img, tf.float32)
# 이미지를 원하는 크기로 조정합니다.
return tf.image.resize(img, [IMG_WIDTH, IMG_HEIGHT])
def process_path(file_path):
label = get_label(file_path)
# 파일에서 raw 데이터를 문자열로 불러옵니다
img = tf.io.read_file(file_path)
img = decode_img(img)
return img, label
Dataset.map
을 이용해서 image, label
쌍으로 이루어진 데이터셋을 만듭니다.
AUTOTUNE = tf.data.experimental.AUTOTUNE
# 여러 이미지를 병렬로 불러오고 처리하도록 `num_parallel_calls` 설정합니다
labeled_ds = list_ds.map(process_path, num_parallel_calls=AUTOTUNE)
for image, label in labeled_ds.take(1):
print("Image shape: ", image.numpy().shape)
print("Label: ", label.numpy())
이 데이터 세트로 모델을 교육하려면 다음과 같은 데이터를 사용하려고 할 것입니다:
tf.data
api를 이용하면 손쉽게 위 특성들을 가진 데이터를 사용할 수 있습니다.
def prepare_for_training(ds, cache=True, shuffle_buffer_size=1000):
# 이는 데이터를 한 번만 불러오고, 이를 메모리에 저장하는 작은 데이터셋입니다.
# 메모리에 맞지 않는 데이터셋의 전처리 작업을 캐시하려면 `.cache(filename)`를 사용하세요.
if cache:
if isinstance(cache, str):
ds = ds.cache(cache)
else:
ds = ds.cache()
ds = ds.shuffle(buffer_size=shuffle_buffer_size)
# 계속 반복합니다.
ds = ds.repeat()
ds = ds.batch(BATCH_SIZE)
# `prefetch`는 모델을 훈련하는 동안
# 데이터셋이 백그라운드에서 배치들을 가져올 수 있도록 해줍니다.
ds = ds.prefetch(buffer_size=AUTOTUNE)
return ds
train_ds = prepare_for_training(labeled_ds)
image_batch, label_batch = next(iter(train_ds))
show_batch(image_batch.numpy(), label_batch.numpy())
불러온 데이터셋의 성능을 확인해봅시다:
import time
default_timeit_steps = 1000
def timeit(ds, steps=default_timeit_steps):
start = time.time()
it = iter(ds)
for i in range(steps):
batch = next(it)
if i%10 == 0:
print('.',end='')
print()
end = time.time()
duration = end-start
print("{} batches: {} s".format(steps, duration))
print("{:0.5f} Images/s".format(BATCH_SIZE*steps/duration))
두 데이터 생성기의 속도를 비교해 봅시다.
# `keras.preprocessing`
timeit(train_data_gen)
# `tf.data`
timeit(train_ds)
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.